
import datetime

from django.db import models
from django.contrib.auth.models import User

#import django.core.validators
#priority_validator = django.core.validators.NumberIsInRange(-100,100)


# TODO:
# Use a table for statuses, with ordered status. Use a relation to this
# table for all the 'status' field.
# Let the status method (dynamic fields) use the Model of this table
# to compute the agregation of children statuses.

class Tag(models.Model):
    name = models.CharField(max_length=64)
    description = models.TextField()
    
    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ('name',)

class Production(models.Model):
    name = models.CharField(max_length=64)
    description = models.TextField()
    
    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ('name',)

class Chantier(models.Model):
    name = models.CharField(max_length=200)
    description = models.TextField()
    
    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ('name',)

class Fonctionalite(models.Model):
    chantier = models.ForeignKey(Chantier)
    name = models.CharField(max_length=200)
    description = models.TextField()
    
    def status(self):
        tacheStatuses = set([ tache.status for tache in self.tache_set.all() ])
        if not tacheStatuses:
            return "Empty"
        if len(tacheStatuses) == 1:
            return tacheStatuses.pop()
        orderedStatus = [ s[0] for s in reversed(Tache.STATUS) ]
        orderedStatus.pop() # get ride of CANCEL
        for status in orderedStatus:
            if status in tacheStatuses:
                return status
        return 'Fonctionalite.status error'
    
    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ('name',)

    def status_report(self):
        '''
        Returns a list an elements like:
            {'label': statusLabel, 'count': nbTaskWithThisStatus, 'percent': percentOfTotalNbTask}
        for each task status.
        '''
        taches_count = self.tache_set.count()
        status_rows = []
        for status, label in Tache.STATUS:
            row = {}
            count = self.tache_set.filter(status=status).count()
            try:
                percent = count*100.0 / taches_count
            except ZeroDivisionError:
                percent = 0
            row['count'] = count
            row['percent'] = percent
            row['label'] = label
            status_rows.append(row)
        return status_rows


class Tache(models.Model):
    STATUS = (
        # DBValue,   DisplayValue
        ('CANCEL',    'Canceled'),
        ('DONE',    'Done'),
        ('2VALID',  'To Valid'),
        ('PAUSE',   'Paused'),
        ('RUN',     'In Progress'),
        ('WAIT',    'Waiting'),
        ('INV',     'BackLogged'),
    )
    fonctionalite = models.ForeignKey(Fonctionalite)
    tags = models.ManyToManyField(Tag, related_name='tagged', blank=True)
    productions = models.ManyToManyField(Production, related_name='tasks', blank=True)
    name = models.CharField(max_length=200)
    creator = models.CharField(max_length=200, blank=True)
    create_date = models.DateField(auto_now_add=True)
    length = models.PositiveSmallIntegerField(default=3,blank=True)
    status = models.CharField(max_length=32, choices=STATUS, default='INV')
    remaining = models.PositiveSmallIntegerField(default=3,blank=True)
    dev = models.ManyToManyField(User, through='TacheDevTime')
    start_date = models.DateField(blank=True, null=True)
    priority = models.PositiveSmallIntegerField(default=50,blank=True, )#validator_list=[priority_validator])
    description = models.TextField()
    
    def in_current_cycle(self):
        return self.tachesducycle_set.filter(cycle__status='CURRENT') and True or False

    def spent(self):
        return sum([ tst.time for tst in self.TacheDevTime_set.all() ])
    
    def __unicode__(self):
        return self.name
    
    class Meta:
        ordering = ('-priority',)

    @classmethod
    def create_in_cycle(cls, name, fonctionalite, cycle, description=''):
        # TODO: this should be inthe Tache manager, through Tache.objects.create_in_cycle
        tache = cls.objects.create(
            name=name,
            fonctionalite=fonctionalite,
            description=description,
            status=cycle and "WAIT" or "INV"
        )
        tache.save()
        if cycle:
            tdc = TachesDuCycle(tache=tache, cycle=cycle)
            tdc.save()
        return tache
    
class TacheDevTime(models.Model):
    tache = models.ForeignKey(Tache)
    dev = models.ForeignKey(User)
    time = models.FloatField(default=0)
    
    class Meta:
        unique_together = (("tache", "dev"),)
        ordering = ('-time',)


class Cycle(models.Model):
    STATUS = (
        # DBValue,   DisplayValue
        ('FUTUR',    '-'),
        ('CURRENT',  'CURRENT'),
        ('DONE',    'Done'),
    )
    name = models.CharField(max_length=200)
    start_date = models.DateField(blank=True)
    end_date = models.DateField(blank=True)
    comments = models.TextField()
    status = models.CharField(max_length=32, choices=STATUS, blank=False, default='FUTUR')
    taches = models.ManyToManyField(Tache, through='TachesDuCycle')
    burndowndata = models.TextField(blank=True, null=True)

    default_len = 14 # nb days in a cycle, used programaticaly
    
    def count(self):
        return len(self.taches.all())
    
    def length(self):
        return sum([ tache.length for tache in self.taches.all() ])
    
    def spent(self):
        return sum([ tache.spent() for tache in self.taches.all() ])
    
    def remaining(self):
        return sum([ tache.remaining() for tache in self.taches.all() ])
    
    def __unicode__(self):
        ret = self.name
        if self.status == 'CURRENT':
            ret = '[ '+ret+' ]'
        return ret

    class Meta:
        ordering = ('start_date',)

    def store_burndown(self):
        '''
        Update burndown data with today's total remaining time.
        '''
        burndown_dict = {}
        if self.burndowndata:
            try:
                exec('burndown_dict = '+self.burndowndata)
            except:
                self.burndowndata = {'BURNDOWDATA_ERROR':`self.burndowndata`}
        today = datetime.date.today()
        burndown_dict[today] = self.remaining()
        self.burndowndata = `burndown_dict`
        self.save()
        
    def status_report(self):
        '''
        Returns a list an elements like:
            {'label': statusLabel, 'count': nbTaskWithThisStatus, 'percent': percentOfTotalNbTask}
        for each task status.
        '''
        taches_count = self.taches.count()
        status_rows = []
        for status, label in Tache.STATUS:
            row = {}
            count = self.taches.filter(status=status).count()
            try:
                percent = count*100.0 / taches_count
            except ZeroDivisionError:
                percent = 0
            row['count'] = count
            row['percent'] = percent
            row['label'] = label
            status_rows.append(row)
        return status_rows


class TachesDuCycle(models.Model):
    tache = models.ForeignKey(Tache)
    cycle = models.ForeignKey(Cycle)
    priority = models.PositiveSmallIntegerField(default=50,blank=True, )#validator_list=[priority_validator])
    
    class Meta:
        unique_together = (("tache", "cycle"),)
        ordering = ('-priority',)

